﻿define([
'Xjs/core/util',
'Xjs/core/Component'
],
function (util, Component) {
    'use strict';

    var type = 'composite',
    Composite = Xjs.getType(type);

    if (Composite) { // It has been already loaded
        return Composite;
    }

    // The component that implements the composite pattern.
    Composite = util.define({
        base: Component,
        construct: function (conf, parent) {

            Composite.base.constructor.call(this, conf, parent);

            if (this.onCreate) { // Called after this component is created but before the children are created
                this.onCreate(conf);
            }

            // The children components of this one
            this.children = [];
            this.createChildren(conf);
        },

        type: type, // Set the new type
        defaultChildType: type, // Set the default type of the child to be created

        // The name of the property in the configuration literal that contains the configuration of the children components
        childrenProperty: 'children',

        // Creates the children of this component from the configuration literal
        createChildren: function (conf) {

            var conf = conf || {},
            children = conf[this.childrenProperty];

            if (!children) { // No children to create
                return;
            }

            // The default values to assign to the children
            var defaults = conf.defaults || {};

            children.forEach(function (childConf) {

                if (childConf.created) { // It is not a configuration object but a created component
                    this.addChild(childConf);
                }
                else {

                    // Apply the defaults members to the child configuration
                    util.augment(childConf, defaults);

                    var type = childConf.type || this.defaultChildType,
                        child = Xjs.create(type, childConf, this);

                    this.addChild(child);

                }
            }, this);
        },

        // Adds a child to this component
        addChild: function (c) {
            c.parent = this;
            this.children.push(c);
        },

        // Visits all the children of the composite in order traversal
        down: function (callback, scope) {

            scope = scope || this;

            if (!scope.children) {
                return;
            }

            scope.children.forEach(function (c) {

                if (callback.call(c) === false) { // Returning false stops the traversal
                    return;
                }

                scope.down(callback, c);
            });
        },

        // Removes all the children of this component
        clear: function () {

            if (this.children) {
                this.children.forEach(function (child) {
                    child.destroy();
                });
            }

            this.children.length = 0; // Clear the array
        },

        destroy: function () {
            this.clear();

            if (this.onBeforeDestroy) { // Called after the children are destroyed but before this component is destroyed
                this.onBeforeDestroy();
            }

            Composite.base.destroy.call(this);
        },

        toString: function () {
            return this.type + ' id:' + (this.id || '[not set]');
        }
    });

    Xjs.registerType(Composite);

    return Composite;
});